/*
 * TcpClient.h
 *
 *  Created on: 2012-11-29
 *      Author: yangentao@gmail.com
 */

#pragma once;
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include "NetUtil.h"
#include "BaseSocket.h"
#include "../Thread.h"
#include "../ThreadHandler.h"
#include "../String.h"
#include "../AutoLock.h"

#define READ_BUF_SIZE	256

namespace yet {

class EventSelect: ThreadLock {
public:
	EventSelect();
	virtual ~EventSelect();
public:
	bool select(BaseSocket* s);
	void cancel();
	bool isSelected() ;
	HANDLE getHandle();
public:
	DWORD readThreadTask(void* argp);
public:
	boost::function<void (BaseSocket* sock, Message msg)> OnSocketEvent;
private:
	HANDLE selectEvent;
	BaseSocket* sock;
	Thread recvThread;
	bool volatile selected;
};

/*
 *Status Changing Map
 *
 * init-->connecting-->connected<---->waitwritable
 *           |             |               |
 *   \       |             |               |
 *    \      |             |               |
 *     \     |             |               |
 *      \    v             v               |
 *      - closed<----------closing<---------
 */

class TcpClient: public ThreadLock {
public:
	enum Status {
		stInit, stConnecting, stConnected, stWaitWritable,
	};
public:
	TcpClient();
	TcpClient(SOCKET s);
	virtual ~TcpClient();
public:
	bool open(string remoteIp, short remotePort);
	//if iswritable() then send
	int send(String buf);
	int send(string buf);
	int send(const char* buf, int size);
	void close();
	void setConnectTimeout(int seconds); //TODO
	bool isWritable();
	bool isWaitWritable();
	bool isConnected();
	bool isConnecting();
	bool isClose();
	Status getStatus();
private:
	void onSocketEvent(BaseSocket* s, Message msg);
	void init(SOCKET s);
	void setStatus(Status st);
public:
	boost::function<void(TcpClient* es, int event, String buf)> OnEvent;
private:
	BaseSocket sock;

	ThreadHandler msgHandler;
	Status status;
	EventSelect selector;
private:
	friend DWORD threadTask(TcpClient* client, void* argp);
};

}
